package com.zzx.boot.servlet.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.zzx.boot.constant.GlobalEnum;
import com.zzx.boot.constant.PreFix;
import com.zzx.boot.constant.Time;
import com.zzx.boot.entity.bind.Bind;
import com.zzx.boot.entity.bind.WeiXin;
import com.zzx.boot.entity.dto.BindLoginDto;
import com.zzx.boot.entity.table.LoginUser;
import com.zzx.boot.entity.table.User;
import com.zzx.boot.entity.table.UserBind;
import com.zzx.boot.entity.table.UserInfo;
import com.zzx.boot.entity.vo.ImageVo;
import com.zzx.boot.entity.vo.TokenVo;
import com.zzx.boot.entity.vo.UserVo;
import com.zzx.boot.exception.SystemctlException;
import com.zzx.boot.mapper.UserMapper;
import com.zzx.boot.servlet.UserBindService;
import com.zzx.boot.servlet.UserInfoService;
import com.zzx.boot.servlet.UserService;
import com.zzx.boot.servlet.UtilsService;
import com.zzx.boot.utils.*;
import kotlin.text.Regex;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author tan
 * @version 1.0
 * @date 2023/7/7 15:10
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {

    private final RedisUtils redisUtils;
    private final SendPhone sendPhone;
    private final UserInfoService userInfoService;
    private final RestTemplate restTemplate;
    private final UserBindService userBindService;
    @Lazy // 不造成循环依赖
    @Autowired
    private AuthenticationManager authenticationManager;
    private final  JWTUtil jwtUtil;
    private final Gson gson;
    private final Transaction transaction;
    private final PasswordEncoder passwordEncoder;
    private final UtilsService utilsService;

    @Override
    public R getCode(String phone) {
        // 判断该手机号是否发送过
        if(redisUtils.isStringEx(PreFix.IS_PHONE+phone)){return R.error(666,"已经发送过验证码了,请等待");}
        // 如果没有发送过
        // 生成随机数
        String code = sendPhone.getRandom();
        // 发送验证码
//        if(!sendPhone.send(phone,code)){
//            return R.error(666,"验证码发送失败!");
//        }
        System.out.println(code);
        redisUtils.setStringEx(PreFix.PHONE_PREFIX+phone,code, 5L, TimeUnit.MINUTES);
        redisUtils.setStringEx(PreFix.IS_PHONE+phone,"1",1L,TimeUnit.MINUTES);
        return R.success(null,"验证码发送成功!");
    }

    @Override
    public R phoneLogin(String phone, String code) {
        User user = getUserByPhoneAndCode(phone, code,new User());
        // 登录
        return R.success(this.createLoginUser(user));
    }

    private User getUserByPhoneAndCode(String phone, String code,User user1) {
        // 判断code相等
        String c = redisUtils.getString(PreFix.PHONE_PREFIX+ phone,String.class);
        if(c==null||!c.equals(code)){
            throw new SystemctlException(GlobalEnum.INCORRECT_PHONE_VERIFICATION_CODE);
        }

        // 判断手机号是否存在
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhone, phone);
        User user = baseMapper.selectOne(wrapper);

        // 注册
        if(Objects.isNull(user)){
         user = transaction.submit(()->{
                String createTime = DateTImeUtils.getDate();
                User u = new User(null, user1.getUsername().equals("")?phone:user1.getUsername(),user1.getUserpic(),
                        passwordEncoder.encode("123456"),
                        phone,
                        user1.getEmail()
                        ,createTime,null);
                baseMapper.insert(u);
                boolean b = userInfoService.save(new UserInfo(null,u.getId(),0,18,"未知","",null,""));
                return u;
            });
        }
        redisUtils.delString(PreFix.PHONE_PREFIX+ phone);
        return user;
    }

    @Override
    public User userByUserName(String username) {

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // 判断是邮箱,还是手机号
        if(new Regex(com.zzx.boot.constant.Regex.PHONE_REGEX).matches(username)||new Regex(com.zzx.boot.constant.Regex.EMAIL_REGEX).matches(username)){
            wrapper.eq(new Regex(com.zzx.boot.constant.Regex.PHONE_REGEX).matches(username),User::getPhone,username);
            wrapper.eq(new Regex(com.zzx.boot.constant.Regex.EMAIL_REGEX).matches(username),User::getEmail,username);
        }else { //昵称
//            wrapper.eq(User::getUsername,username);
            throw new SystemctlException(GlobalEnum.USERNAME_OR_PASSWORD_ERROR);
        }

        User user = baseMapper.selectOne(wrapper);
        if(Objects.isNull(user)){
            throw new SystemctlException(GlobalEnum.USER_IS_NOT_REGISTERED);
        }
        return user;
    }

    @Override
    public UserBind bindLogin(BindLoginDto loginDto) {
        UserBind userBind = null;
       try{
           // 判断登录类型
           Class clazz = Bind.map.get(loginDto.getType());
           Bind bind = (Bind) clazz.getDeclaredConstructor(restTemplate.getClass()).newInstance(restTemplate);
               userBind = bind.empower(loginDto);
               UserBind u = userBindService.getOne(new LambdaQueryWrapper<UserBind>().eq(UserBind::getOpenid, userBind.getOpenid()));
           if(Objects.isNull(u)){
               // 判断是否存在了
               userBindService.save(userBind);

           }else {
               userBind = u;
           }
       }catch (Exception e){
           e.printStackTrace();
       }
       if(Objects.isNull(userBind)){
           throw new  SystemctlException(GlobalEnum.LOGIN_ERROR);
       }
        return userBind;
    }

    @Override
    public R logout() {
        // 清除redis
        redisUtils.delString(PreFix.LOGIN_PREFIX+SecurityUtils.getUserId());
        return R.success("退出登录成功");
    }

    @Override
    public R securityLogin(String username, String password) {
        // 创建token
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authenticate = authenticationManager.authenticate(token);
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        if(Objects.isNull(loginUser)){
            throw new SystemctlException(GlobalEnum.USERNAME_OR_PASSWORD_ERROR);
        }
        TokenVo vo = createToken(loginUser);
        return R.success(vo);
    }

    @NotNull
    private TokenVo createToken(LoginUser loginUser) {
        // 生成token
        String jwtToken = jwtUtil.createToken(loginUser.getUser().getId(), Time.TOKEN_TIME);
        // 生成刷新token
        String accessToken = jwtUtil.createToken(loginUser.getUser().getId(),Time.ACCESS_TOKEN_TIME);
        TokenVo vo = new TokenVo(jwtToken,accessToken);
        // 存储到reids中
        redisUtils.setStringEx(PreFix.LOGIN_PREFIX+ loginUser.getUser().getId(),gson.toJson(loginUser),Time.TOKEN_TIME.longValue(),TimeUnit.SECONDS);
        return vo;
    }

    @Override
    public UserVo getUserById(Long userId) {
        // 获取用户信息
        User user = baseMapper.selectById(userId);
        // 获取用户资料
        UserInfo userInfo = userInfoService.getOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getUserId,userId));
        if(Objects.isNull(userInfo)){
            return new UserVo(userId.toString(),user.getUsername(),user.getUserpic(),null,null);
        }
        UserVo vo = new UserVo(userId.toString(),user.getUsername(),
                user.getUserpic(),
                userInfo.getSex(),
                userInfo.getAge());
        return vo;
    }

    @Override
    public UserVo userSearch(String keyword) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // 判断是手机号还是邮箱或者是昵称
        if(new Regex(com.zzx.boot.constant.Regex.PHONE_REGEX).matches(keyword)||new Regex(com.zzx.boot.constant.Regex.EMAIL_REGEX).matches(keyword)){
            wrapper.eq(new Regex(com.zzx.boot.constant.Regex.PHONE_REGEX).matches(keyword),User::getPhone,keyword);
            wrapper.eq(new Regex(com.zzx.boot.constant.Regex.EMAIL_REGEX).matches(keyword),User::getEmail,keyword);
        }else { //昵称
            wrapper.eq(User::getUsername,keyword);
        }
        User user = baseMapper.selectOne(wrapper);
        if(Objects.isNull(user)){
            return null;
        }
        // 获取用户信息和资料
        UserInfo userInfo = userInfoService.getOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getUserId, user.getId()));
        UserVo vo = new UserVo(user.getId().toString(), user.getUsername(),
                user.getUserpic(),
                userInfo.getSex(),
                userInfo.getAge());
        return vo;
    }

    @Override
    public R bindPhone(String phone, String code, Long bindId) {
        User user1 =  transaction.submit(()->{
            // 校验第三方登录id存在
            UserBind userBind = userBindService.getById(bindId);
            if(userBind.getUserId()!=0){
                throw new SystemctlException(GlobalEnum.BINDUSER_ERROR);
            }
            User u = new User();
            u.setUsername(userBind.getNickname());
            u.setUserpic(userBind.getAvatarurl());
            // 通过手机号和验证码创建用户
            User user = getUserByPhoneAndCode(phone, code,u);
            userBind.setUserId(user.getId());
            userBindService.updateById(userBind);
            return user;
        });
        TokenVo token = this.createLoginUser(user1);
        return R.success(token);
    }

    @Override
    public String bindUser(BindLoginDto loginDto) {
       boolean b = transaction.submit(()->{
            // 获取用户id
            Long id = SecurityUtils.getUserId();
            // 创建第三方登录
            UserBind userBind = this.bindLogin(loginDto);
            userBind.setUserId(id);
            return userBindService.updateById(userBind);
        });
        return b?"绑定成功":"绑定失败";
    }

    @Override
    public TokenVo createLoginUser(User user) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUser(user);
        return this.createToken(loginUser);
    }

    @Override
    public String updateAvatar(MultipartFile file) {
        ImageVo imageVo = utilsService.upload(file);
        User user = SecurityUtils.getLoginUser().getUser();
        user.setUserpic(imageVo.getUrl());
        baseMapper.updateById(user);
        // 更新redis中的数据
        redisUtils.setStringEx(PreFix.LOGIN_PREFIX+ user.getId(),
                gson.toJson(SecurityUtils.getLoginUser()),
                        Time.TOKEN_TIME.longValue(),
                        TimeUnit.SECONDS);

        return imageVo.getUrl();
    }

    @Override
    public String updatePassword(String oldPassword, String newPassword,String newPassword2) {
        // 判断两次密码是否一致
        if(!newPassword.equals(newPassword2)){
            throw new SystemctlException(GlobalEnum.OLDPASSWORD_AND_NEWPASSWORD_INCONSISTENT);
        }
        // 获取当前密码
        User user = SecurityUtils.getLoginUser().getUser();
        // 判断密码是否正确
        boolean matches = passwordEncoder.matches(oldPassword, user.getPassword());
        if(!matches){
            throw new SystemctlException(GlobalEnum.PASSWORD_ERROR);
        }
        user.setPassword(passwordEncoder.encode(newPassword));
        int i = baseMapper.updateById(user);
        // 更新redis中的数据
        redisUtils.setStringEx(PreFix.LOGIN_PREFIX+ user.getId(),
                gson.toJson(SecurityUtils.getLoginUser()),
                Time.TOKEN_TIME.longValue(),
                TimeUnit.SECONDS);
        return i==1?"密码更新成功":"密码更新失败";
    }
}
